/*********************************************************************
 * Copyright (C) 2002 Andrew Khan
 * <p>
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * <p>
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * <p>
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 ***************************************************************************/

package jxl.read.biff;

import jxl.*;
import jxl.biff.IntegerHelper;
import jxl.biff.RecordData;
import jxl.biff.SheetRangeImpl;
import jxl.biff.StringHelper;
import jxl.common.Logger;

import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;

/**
 * A number record.  This is stored as 8 bytes, as opposed to the
 * 4 byte RK record
 */
public class HyperlinkRecord extends RecordData implements Hyperlink {
    /**
     * The logger
     */
    private static Logger logger = Logger.getLogger(HyperlinkRecord.class);

    /**
     * The first row
     */
    private int firstRow;
    /**
     * The last row
     */
    private int lastRow;
    /**
     * The first column
     */
    private int firstColumn;
    /**
     * The last column
     */
    private int lastColumn;

    /**
     * The URL referred to by this hyperlink
     */
    private URL url;

    /**
     * The local file referred to by this hyperlink
     */
    private File file;

    /**
     * The location in this workbook referred to by this hyperlink
     */
    private String location;

    /**
     * The range of cells which activate this hyperlink
     */
    private SheetRangeImpl range;

    /**
     * The type of this hyperlink
     */
    private LinkType linkType;

    /**
     * The excel type of hyperlink
     */
    private static class LinkType {
    }

    ;

    private static final LinkType urlLink = new LinkType();
    private static final LinkType fileLink = new LinkType();
    private static final LinkType workbookLink = new LinkType();
    private static final LinkType unknown = new LinkType();

    /**
     * Constructs this object from the raw data
     *
     * @param t the raw data
     * @param s the sheet
     * @param ws the workbook settings
     */
    HyperlinkRecord(Record t, Sheet s, WorkbookSettings ws) {
        super(t);

        linkType = unknown;

        byte[] data = getRecord().getData();

        // Build up the range of cells occupied by this hyperlink
        firstRow = IntegerHelper.getInt(data[0], data[1]);
        lastRow = IntegerHelper.getInt(data[2], data[3]);
        firstColumn = IntegerHelper.getInt(data[4], data[5]);
        lastColumn = IntegerHelper.getInt(data[6], data[7]);
        range = new SheetRangeImpl(s,
                firstColumn, firstRow,
                lastColumn, lastRow);

        int options = IntegerHelper.getInt(data[28], data[29], data[30], data[31]);

        boolean description = (options & 0x14) != 0;
        int startpos = 32;
        int descbytes = 0;
        if (description) {
            int descchars = IntegerHelper.getInt
                    (data[startpos], data[startpos + 1],
                            data[startpos + 2], data[startpos + 3]);
            descbytes = descchars * 2 + 4;
        }

        startpos += descbytes;

        boolean targetFrame = (options & 0x80) != 0;
        int targetbytes = 0;
        if (targetFrame) {
            int targetchars = IntegerHelper.getInt
                    (data[startpos], data[startpos + 1],
                            data[startpos + 2], data[startpos + 3]);
            targetbytes = targetchars * 2 + 4;
        }

        startpos += targetbytes;

        // Try and determine the type
        if ((options & 0x3) == 0x03) {
            linkType = urlLink;

            // check the guid monicker
            if (data[startpos] == 0x03) {
                linkType = fileLink;
            }
        } else if ((options & 0x01) != 0) {
            linkType = fileLink;
            // check the guid monicker
            if (data[startpos] == (byte) 0xe0) {
                linkType = urlLink;
            }
        } else if ((options & 0x08) != 0) {
            linkType = workbookLink;
        }

        // Try and determine the type
        if (linkType == urlLink) {
            String urlString = null;
            try {
                startpos += 16;

                // Get the url, ignoring the 0 char at the end
                int bytes = IntegerHelper.getInt(data[startpos],
                        data[startpos + 1],
                        data[startpos + 2],
                        data[startpos + 3]);

                urlString = StringHelper.getUnicodeString(data, bytes / 2 - 1,
                        startpos + 4);
                url = new URL(urlString);
            } catch (MalformedURLException e) {
                logger.warn("URL " + urlString + " is malformed.  Trying a file");
                try {
                    linkType = fileLink;
                    file = new File(urlString);
                } catch (Exception e3) {
                    logger.warn("Cannot set to file.  Setting a default URL");

                    // Set a default URL
                    try {
                        linkType = urlLink;
                        url = new URL("http://www.andykhan.com/jexcelapi/index.html");
                    } catch (MalformedURLException e2) {
                        // fail silently
                    }
                }
            } catch (Throwable e) {
                StringBuffer sb1 = new StringBuffer();
                StringBuffer sb2 = new StringBuffer();
                CellReferenceHelper.getCellReference(firstColumn, firstRow, sb1);
                CellReferenceHelper.getCellReference(lastColumn, lastRow, sb2);
                sb1.insert(0, "Exception when parsing URL ");
                sb1.append('\"').append(sb2.toString()).append("\".  Using default.");
                logger.warn(sb1, e);

                // Set a default URL
                try {
                    url = new URL("http://www.andykhan.com/jexcelapi/index.html");
                } catch (MalformedURLException e2) {
                    // fail silently
                }
            }
        } else if (linkType == fileLink) {
            try {
                startpos += 16;

                // Get the name of the local file, ignoring the zero character at the
                // end
                int upLevelCount = IntegerHelper.getInt(data[startpos],
                        data[startpos + 1]);
                int chars = IntegerHelper.getInt(data[startpos + 2],
                        data[startpos + 3],
                        data[startpos + 4],
                        data[startpos + 5]);
                String fileName = StringHelper.getString(data, chars - 1,
                        startpos + 6, ws);

                StringBuffer sb = new StringBuffer();

                for (int i = 0; i < upLevelCount; i++) {
                    sb.append("..\\");
                }

                sb.append(fileName);

                file = new File(sb.toString());
            } catch (Throwable e) {
                logger.warn("Exception when parsing file " +
                        e.getClass().getName() + ".");
                file = new File(".");
            }
        } else if (linkType == workbookLink) {
            int chars = IntegerHelper.getInt(data[32], data[33], data[34], data[35]);
            location = StringHelper.getUnicodeString(data, chars - 1, 36);
        } else {
            // give up
            logger.warn("Cannot determine link type");
            return;
        }
    }

    /**
     * Determines whether this is a hyperlink to a file
     *
     * @return TRUE if this is a hyperlink to a file, FALSE otherwise
     */
    public boolean isFile() {
        return linkType == fileLink;
    }

    /**
     * Determines whether this is a hyperlink to a web resource
     *
     * @return TRUE if this is a URL
     */
    public boolean isURL() {
        return linkType == urlLink;
    }

    /**
     * Determines whether this is a hyperlink to a location in this workbook
     *
     * @return TRUE if this is a link to an internal location
     */
    public boolean isLocation() {
        return linkType == workbookLink;
    }

    /**
     * Returns the row number of the top left cell
     *
     * @return the row number of this cell
     */
    public int getRow() {
        return firstRow;
    }

    /**
     * Returns the column number of the top left cell
     *
     * @return the column number of this cell
     */
    public int getColumn() {
        return firstColumn;
    }

    /**
     * Returns the row number of the bottom right cell
     *
     * @return the row number of this cell
     */
    public int getLastRow() {
        return lastRow;
    }

    /**
     * Returns the column number of the bottom right cell
     *
     * @return the column number of this cell
     */
    public int getLastColumn() {
        return lastColumn;
    }

    /**
     * Gets the URL referenced by this Hyperlink
     *
     * @return the URL, or NULL if this hyperlink is not a URL
     */
    public URL getURL() {
        return url;
    }

    /**
     * Returns the local file eferenced by this Hyperlink
     *
     * @return the file, or NULL if this hyperlink is not a file
     */
    public File getFile() {
        return file;
    }

    /**
     * Exposes the base class method.  This is used when copying hyperlinks
     *
     * @return the Record data
     */
    public Record getRecord() {
        return super.getRecord();
    }

    /**
     * Gets the range of cells which activate this hyperlink
     * The get sheet index methods will all return -1, because the
     * cells will all be present on the same sheet
     *
     * @return the range of cells which activate the hyperlink
     */
    public Range getRange() {
        return range;
    }

    /**
     * Gets the location referenced by this hyperlink
     *
     * @return the location
     */
    public String getLocation() {
        return location;
    }
}







